using System;
using ilgolosone.Shared.Data;
using ilgolosone.Shared.Exceptions;
using NHibernate;

namespace ilgolosone.Persistence.NH.Data.Impl
{
    public class NHibernateUnitOfWork : INHibernateUnitOfWork
    {
        private readonly ISessionCreator _source;
        private bool _isDisposed;
        private bool _isInitialized;
        private ITransaction _transaction;

        public NHibernateUnitOfWork(ISessionCreator source)
        {
            _source = source;
        }

        #region INHibernateUnitOfWork Members

        public ISession CurrentSession { get; private set; }

        #endregion

        #region IUnitOfWork Members

        public IUnitOfWork Start()
        {
            ShouldNotCurrentlyBeDisposed();

            CurrentSession = _source.CreateSession();
            
            BeginNewTransaction();

            _isInitialized = true;

            return this;
        }

        public void Flush()
        {
            ShouldNotCurrentlyBeDisposed();
            
            ShouldBeStartedFirst();
            
            CurrentSession.Flush();
        }

        public void Commit()
        {
            ShouldNotCurrentlyBeDisposed();

            ShouldBeStartedFirst();

            _transaction.Commit();
        }

        public void Rollback()
        {
            ShouldNotCurrentlyBeDisposed();

            ShouldBeStartedFirst();

            _transaction.Rollback();
        }

        public void Dispose()
        {
            if (_isDisposed || !_isInitialized) return;

            _transaction.Dispose();

            CurrentSession.Dispose();

            _isDisposed = true;
        }

        #endregion

        #region Metodi privati

        private void BeginNewTransaction()
        {
            if (_transaction != null)
            {
                _transaction.Dispose();
            }

            _transaction = CurrentSession.BeginTransaction();
        }

        private void ShouldNotCurrentlyBeDisposed()
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
        }

        private void ShouldBeStartedFirst()
        {
            if (!_isInitialized)
            {
                throw new UnitOfWorkNotStartedException();
            }
        } 

        #endregion
    }
}